/**
 *
 */
package 场景.瞬间高并发请求.redis;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang3.StringUtils;

import redis.clients.jedis.Jedis;

/**
 *
 * @Title:通过先请的放入队列，异步从队列获取请求连接，再进行数据库操作（同一个商品串行化）
 * @author zhoujie
 * @date 2019年1月12日 下午1:51:45
 *
 */
public class Seckill03 {
    static final int MAX_THREAD = 100000;

    static final int Inv = 5;
    static InvDao dto = new InvDao(Inv);// 数据库dao

    static final String tokenkeys = "token:goods:1";

    static CountDownLatch latch = new CountDownLatch(MAX_THREAD);
    static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(Inv);//对列大小等于库存数

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(20);
        ExecutorService outExe = Executors.newFixedThreadPool(1);

        Jedis redis = new Jedis("127.0.0.1", 6379);
        redis.del("setsucc");
        redis.del(tokenkeys);
        redis.del("goods:lock:2");

        Runnable outRun = () -> outInv();
        outExe.execute(outRun);//启动消费者
        //redis.del("goods:1");//清除用户标记
        for (int i = 0; i < MAX_THREAD; i++) {
            final int id = i;
            Runnable run = () -> doWork(id);// 都是调用doWork方法
            executor.submit(run);
            latch.countDown();
        }
        executor.shutdown();
        while (true) {
            if (executor.isTerminated()) {
                break;
            }
        }
        System.out.println("秒杀结束当前库存：" + dto.getInv());
        System.out.println("数据库总访问次数：" + dto.getCount().intValue());
        System.out.println("抢到商品的用户个数：" + redis.smembers("setsucc").size());
        System.exit(0);
    }

    public static void doWork(int id) {
        Jedis redis = new Jedis("127.0.0.1", 6379);
        try {
            latch.await();// 等线程都就绪后才开始执行
            // 防止重复购买（这里模拟环境其实不会）
            //if (redis.sadd("goods:1", "thread:" + id) == 1) {
            if (!queue.offer(id)) {
                System.out.println("当前人数过多");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            redis.close();
        }

    }


    /**定义一个执行减库存操作(同一个商品串行化操作)
     * @param redis
     * @throws InterruptedException
     */
    private static void outInv() {
        Jedis redis = new Jedis("127.0.0.1", 6379);
        while (true) {
            Integer id = null;
            try {
                //一直阻塞如果没有
                id = queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String lock = redis.get("goods:lock:2");
            if (!"1".equals(lock) && dto.outInv(1) > 0) {//这里会n+1次访问数据库 最后一次是刚好没库存的
                System.out.println("恭喜你秒杀到商品，用户id:" + id);
                redis.sadd("setsucc", id + "");
            } else {
                redis.set("goods:lock:2", "1");//后面的不需要继续访问数据库了
                System.out.println("商品已经被秒杀完");
            }
        }
    }
}
